/* eslint-disable @typescript-eslint/no-unsafe-return */
import _ from 'lodash';
import {
  HerinFind,
  HerinProject,
  HerinSort,
} from '../../types/type/request.type';
import { SortEnum } from './param.enum';
import {
  EitherParam,
  JoinParam,
  MatchParam,
  PageParam,
  PageStructure,
  ParamHandle,
  ProjectParam,
  QueryBodyParam,
  QueryParam,
  QueryParamType,
  RangeParam,
  SearchParam,
  SortParam,
} from './param.type';
import mongoose from 'mongoose';
import dayjs from 'dayjs';
export class ParamFormat {
  public static format(query: QueryParam): ParamHandle {
    let returnData = { skip: 0, limit: 10 };

    // if (_.isEmpty(query)) {
    //   return returnData;
    // }

    if (query == undefined || query == null || JSON.stringify(query) === '{}') {
      return returnData;
    }

    const pageFormate = this._formatPageAndPerpage(query.page, query.perpage);

    returnData = {
      limit: pageFormate.limit,
      skip: pageFormate.skip,
      // sort: this._formatSort(query.sort),
      // findObj: {
      //   ...this._formateSearch(query.search),
      //   ...this._formateMatch(query.match),
      //   ...this._formateRange(query.range),
      //   ...this._formateEither(query.or),
      // },
      // project: this._formateProject(query.project),
      // join: this._formatJoin(query.join),
      // joinProject: this._formatJoinProject(query.joinProject),
      // either: this._formateEither(query.either),
    };

    return returnData;
  }

  public static formatBobyParam(query: QueryBodyParam) {
    let returnData = {
      skip: 0,
      limit: 10,
      findObj: {},
      project: {},
      sort: {},
      join: {},
      joinProject: {},
      other: {},
    };

    if (_.isEmpty(query)) return returnData;

    const pageFormate = this._formatPageAndPerpage(query.page, query.perpage);
    returnData = {
      limit: pageFormate.limit,
      skip: pageFormate.skip,
      sort: this._formatSort(query.sort),
      findObj: { ...this._formateBobyMatch(query.match) },
      project: this._formateProject(query.project),
      join: this._formatJoin(query.join),
      joinProject: this._formatJoinProject(query.joinProject),
      // either: this._formateEither(query.either),
      other: query.other,
    };

    return returnData;
  }

  // 分页处理
  private static _formatPageAndPerpage(
    page?: PageParam,
    perpage?: PageParam,
  ): PageStructure {
    const PAGE_DEFAULT = 1;
    const PERPAGE_DEFAULT = 10;
    // 判断不存在
    if (!page) page = PAGE_DEFAULT;
    if (!perpage) perpage = PERPAGE_DEFAULT;
    // 转换数字
    if (typeof page === 'string') page = parseInt(page);
    if (typeof perpage === 'string') perpage = parseInt(perpage);
    // 转换失败
    if (isNaN(page)) page = PAGE_DEFAULT;
    if (isNaN(perpage)) perpage = PERPAGE_DEFAULT;

    const skip = (page - 1) * perpage;
    const limit = perpage;

    return { skip, limit };
  }

  // keyword转义
  private static _escapeHandle(keyword?: string): string {
    if (keyword && keyword !== '.')
      keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
    if (!keyword) keyword = '';

    return keyword;
  }

  // 处理列表排序
  private static _formatSort(sort: SortParam): HerinSort {
    const sortObj = {};
    // 默认进行排序
    if (_.isEmpty(sort)) return { createdAt: -1 };
    if (!Array.isArray(sort)) sort = [sort];

    for (const o of sort) {
      const [field, order] = o.split(',');
      if (!field || !order) continue;
      sortObj[field] = (order as SortEnum) === SortEnum.ASC ? 1 : -1;
    }

    return sortObj;
  }

  // 处理模糊搜索
  private static _formateSearch(search: SearchParam): HerinFind {
    const findObj = {};
    if (_.isEmpty(search)) return findObj;

    if (!Array.isArray(search)) search = [search];
    for (const o of search) {
      const [field, value] = o.split(',');
      if (!field || !value) continue;
      findObj[field] = { $regex: this._escapeHandle(value) };
    }
    return findObj;
  }

  private static _formateBobyMatch(match: QueryParamType) {
    const findObj = {};

    const andConditions: any[] = [];

    if (_.isEmpty(match)) return findObj;
    if (!Array.isArray(match)) match = [match];

    for (const condition of match) {
      const { column, operator, value } = condition;

      if (!column || value === undefined) continue;

      let processedValue: any = value;

      // 处理 ObjectId
      if (
        column === '_id' &&
        operator === 'in' &&
        Array.isArray(processedValue)
      ) {
        processedValue = processedValue.map(
          (id) => new mongoose.Types.ObjectId(id.toString()),
        );
      }
      if (column === '_id' && operator === '=' && processedValue) {
        processedValue = new mongoose.Types.ObjectId(processedValue.toString());
      }

      if (this._isDateString(value)) {
        processedValue = this._isDateString(value);
      }

      // 处理多字段 or 查询
      if (Array.isArray(column) && operator === 'or') {
        const fieldOrConditions: any[] = [];
        for (const col of column) {
          const orFindObj: any = {};
          if (typeof processedValue === 'string') {
            orFindObj[col] = { $regex: new RegExp(processedValue, 'i') };
          } else {
            orFindObj[col] = processedValue;
          }
          fieldOrConditions.push(orFindObj);
        }
        andConditions.push({ $or: fieldOrConditions });
      } else if (operator === 'or' && Array.isArray(value)) {
        // 处理多个字段的 or 条件
        for (const orCondition of value) {
          const orFindObj = this._formateBobyMatch([orCondition]);
          andConditions.push(orFindObj);
        }
      } else {
        // 根据操作符构建查询条件
        if (operator === '=') {
          findObj[column] = processedValue;
        } else if (operator === 'in' && Array.isArray(processedValue)) {
          findObj[column] = { $in: processedValue };
        } else if (operator === 'nin' && Array.isArray(processedValue)) {
          findObj[column] = { $nin: processedValue };
        } else if (operator === 'ne') {
          findObj[column] = { $ne: processedValue };
        } else if (operator === 'gt') {
          andConditions.push({ [column]: { $gt: processedValue } });
        } else if (operator === 'gte') {
          andConditions.push({ [column]: { $gte: processedValue } });
        } else if (operator === 'lt') {
          andConditions.push({ [column]: { $lt: processedValue } });
        } else if (operator === 'lte') {
          andConditions.push({ [column]: { $lte: processedValue } });
        } else if (operator === 'like' && typeof processedValue === 'string') {
          // 模糊匹配，使用正则表达式
          findObj[column] = { $regex: new RegExp(processedValue, 'i') };
        } else if (operator === 'boolean') {
          // 字符串转成布尔
          if (processedValue === 'true') {
            findObj[column] = true;
          } else if (processedValue === 'false') {
            findObj[column] = false;
          }
        }
      }
    }

    if (andConditions.length > 0) {
      findObj['and'] = andConditions;
    }

    return findObj;
  }
  // 处理直接匹配
  private static _formateMatch(match: MatchParam): HerinFind {
    const findObj = {};
    if (_.isEmpty(match)) return findObj;

    if (!Array.isArray(match)) match = [match];
    for (const o of match) {
      const str = o.split(',');
      const field = str[0];
      let value: unknown = str[1];

      if (!field || !value) continue;

      if (value.toString() === 'true') value = true;
      else if (value.toString() === 'false') value = false;

      findObj[field] = value;
    }

    return findObj;
  }

  // 处理范围查询
  private static _formateRange(range: RangeParam): HerinFind {
    const rangeObj = {};
    if (_.isEmpty(range)) return rangeObj;

    if (!Array.isArray(range)) range = [range];
    for (const o of range) {
      const [field, value, condition] = o.split(',');

      if (!field || !condition || !value) continue;
      // 判断对象内是否有某个属性,因为查询范围可能存在一个字段同时包含$lte和$gte
      if (rangeObj.hasOwnProperty(field)) {
        rangeObj[field]['$' + condition] = value;
      } else {
        rangeObj[field] = { ['$' + condition]: value };
      }
    }

    return rangeObj;
  }

  // 处理or查询
  private static _formateEither(or: EitherParam): HerinFind {
    const eitherObj = {};
    if (_.isEmpty(or)) return eitherObj;

    if (!Array.isArray(or)) or = [or];

    const either = [];

    for (const o of or) {
      const str = o.split(',');
      const field = str[0];
      let value: unknown = str[1];

      if (!field || !value) continue;

      if (value.toString() === 'true') value = true;
      else if (value.toString() === 'false') value = false;

      either.push({ [field]: Number(value) });
    }
    eitherObj['$or'] = either;

    return eitherObj;
  }

  // 处理字段返回
  private static _formateProject(project: ProjectParam): HerinProject {
    const projectObj = {};
    if (_.isEmpty(project)) return projectObj;

    if (!Array.isArray(project)) project = [project];
    for (const item of project) {
      const field = item.split(',');
      for (const o of field) {
        projectObj[o] = 1;
      }
    }

    return projectObj;
  }

  private static _formatJoin(join: JoinParam) {
    if (!join) return {};

    const { from, localField, foreignField, as } = join;

    if (!from || !localField || !foreignField || !as) return {};

    return {
      $lookup: {
        from,
        localField,
        foreignField,
        as,
      },
    };
  }

  private static _formatJoinProject(
    joinProject: string[],
  ): Record<string, number> {
    if (!joinProject) return {};

    const projectObj: Record<string, number> = {};

    if (!Array.isArray(joinProject)) joinProject = [joinProject];
    for (const item of joinProject) {
      const field = item.split(',');
      for (const o of field) {
        projectObj[o] = 1;
      }
    }

    return projectObj;
  }

  private static _isDateString(value: string): boolean | Date {
    // 支持的日期格式
    const dateFormats = [
      'YYYY-MM-DD',
      'YYYY-MM-DD HH:mm:ss.sss',
      'YYYY/MM/DD HH:mm:ss',
      'YYYY-MM-DD HH:mm:ss',
      'YYYY.MM.DD HH:mm:ss',
      'YYYYMMDDHHmmss',
      'YYYY/MM/DD',
      'YYYY.MM.DD',
      'YYYYMMDD',
      'MM/DD/YYYY',
      'MM-DD-YYYY',
      'MM.DD.YYYY',
      'DD/MM/YYYY',
      'DD-MM-YYYY',
      'DD.MM.YYYY',
    ];

    for (const format of dateFormats) {
      if (dayjs(value, format, true).isValid()) {
        return new Date(value);
      }
    }

    return false;
  }
}
